#include<linux/delay.h>
#include<asm/io.h>
#include <mach/regs-gpio.h>
#include<linux/module.h>
#include<linux/cdev.h>
#include<linux/fs.h>

#include<linux/interrupt.h>
#include<linux/sched.h>
#include<mach/irqs.h>

#include<asm/uaccess.h>

static struct cdev *buttons_cdev;
static int press_cnt[4];    /* press conunter */
static dev_t buttons_devno;

struct buttons_irq_desc {
    int irq;                /* interrupt vector */
    unsigned long flags;    /* user-defined trigger-mode */
    char *name;             /* interrupt name */
};

/* define the external-interrupt relates to interrupt-mode and pin-name */
static struct buttons_irq_desc buttons_irqs[] = {
    {IRQ_EINT0, IRQF_TRIGGER_FALLING, "S2"},
    {IRQ_EINT2, IRQF_TRIGGER_FALLING, "S3"},
    {IRQ_EINT11, IRQF_TRIGGER_FALLING, "S4"},
    {IRQ_EINT19, IRQF_TRIGGER_FALLING, "S5"},
};
/* available irq-vector is defined in <mach/irqs.h>, <plat/irqs.h> */

/* define/declare a wait queue */
static DECLARE_WAIT_QUEUE_HEAD(buttons_wait_queue);

/* interrupt-event-flag,
 * be setted to 1 in interrupt-service,
 * be setted to 0 in button_read() */
static volatile int event_press = 0;

/* core-interrupt handler for 4 buttons */
static irqreturn_t buttons_interrupt(int irq,
        void *dev_id) {
    switch(irq) {
        case IRQ_EINT0:
            printk(KERN_ALERT"S2 button has been pressed\n");
            break;
        case IRQ_EINT2:
            printk(KERN_ALERT"S3 button has been pressed\n");
            break;
        case IRQ_EINT11:
            printk(KERN_ALERT"S4 button has been pressed\n");
            break;
        case IRQ_EINT19:
            printk(KERN_ALERT"S5 button has been pressed\n");
            break;
    }
    event_press = 1;
    wake_up_interruptible(&buttons_wait_queue);
    return IRQ_RETVAL(IRQ_HANDLED);
}

#if 0
static int button_open(struct inode *inode,
        struct file *filp) {
    int i;
    int err;

    for(i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
        /* register the interrupt-handler */
        err = request_irq(button_irqs[i].irq, button_interrupt,
                button_irqs[i].flags, button_irqs[i].name, (void *)&press_cnt[i]);
        if (err)
            break;
    }

    if (err) {
        /* if error occurs, free the registered interrupt */
        i--;
        for(; i >= 0; i--)
            free_irq(button_irqs[i].irq, (void *)&press_cnt[i]);
        return -EBUSY;
    }

    return 0;
}

static int button_release(struct inode *inode,
        struct file *filp) {
    int i;

    for(i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
        /* free the registered interrupt */
        free_irq(button_irqs[0].irq, (void *)&press_cnt[i]);
    }
    return 0;
}

static int button_read(struct file *filp,
        char __user *buff,
        size_t count,
        loff_t *offp) {
    unsigned long err;
    /* if event_press == 0, which means no-button-pressed, then suspend */
    wait_event_interruptible(button_wait_queue, event_press);

    /* if wait_event == 1, 
     * then the above func() will be return
     * clear the 'event_press' */
    event_press = 0;

    /* give back the button-press-status to the user,
     * and clear */
    err = copy_to_user(buff, (const void *)press_cnt, min(sizeof(press_cnt), count));

    memset((void *)press_cnt, 0, sizeof(press_cnt));

    return err ? -EFAULT : 0;
}

static struct file_operations button_fops = {
    .owner = THIS_MODULE,
    .open = button_open,
    .release = button_release,
    .read = button_read,
};
#endif

static int buttons_setup(void) {
    int i;
    int err;

    for(i = 0; i < sizeof(buttons_irqs)/sizeof(buttons_irqs[0]); i++) {
        /* register the interrupt-handler */
        err = request_irq(buttons_irqs[i].irq, buttons_interrupt,
                buttons_irqs[i].flags, buttons_irqs[i].name, (void *)&press_cnt[i]);
        if (err)
            break;
    }

    if (err) {
        /* if error occurs, free the registered interrupt */
        i--;
        for(; i >= 0; i--)
            free_irq(buttons_irqs[i].irq, (void *)&press_cnt[i]);
        return -EBUSY;
    }

    return 0;
}

static int buttons_free(void) {
    int i;

    for(i = 0; i < sizeof(buttons_irqs)/sizeof(buttons_irqs[0]); i++) {
        /* free the registered interrupt */
        free_irq(buttons_irqs[i].irq, (void *)&press_cnt[i]);
    }
    return 0;

}

static int __init buttons_init(void) {
    int ret;
    ret = alloc_chrdev_region(&buttons_devno, 0, 4, "buttons");
    if(ret < 0) {
        printk("Can't register major number\n");
        return ret;
    }

    buttons_cdev = cdev_alloc();
    if(!buttons_cdev) {
        printk("Can't register major number\n");
        return -EAGAIN;
    }
    // button_cdev->ops = &button_fops;
    buttons_cdev->owner = THIS_MODULE;
    buttons_cdev->dev = buttons_devno;

    ret = cdev_add(buttons_cdev, buttons_devno, 1);

    buttons_setup();
    printk("The button has been initialized, waiting to be pressed\n");
    return 0;
}

static void __exit buttons_cleanup(void) {
    cdev_del(buttons_cdev);
    buttons_free();
    unregister_chrdev_region(buttons_devno, 2);
}

module_init(buttons_init);
module_exit(buttons_cleanup);
